#include "receiver.h"
#include <QDomDocument>
#include <QNetworkReply>
#include <QStringList>

#include <iostream> //debug
using std::cout;
using std::endl;

 //Helper Function (Code generation)
	#define getDomNodeValue(parent, name, sname, func) \
		QDomNodeList dom##name = dom##parent.item(0).toElement().elementsByTagName(QString(sname)); \
		if(dom##name .size()) { \
			name = dom##name.item(0).toElement().text()func; }

	#define getDomNodeValue2(parent, name, sname, func) \
		QDomNodeList dom##name = dom##parent.toElement().elementsByTagName(QString(sname)); \
		if(dom##name .size()) { \
			name = dom##name.item(0).toElement().text()func; }

	#define queryAnswer(type,stype) \
		query = xml.elementsByTagName(QString("answer") + QString(stype)); \
    if(query.size()) \
			return answer##type(input)

namespace Tubox {

/**
 * <answerLogin>
 *   <status>0</status>
 * </answerLogin>
 */

void Receiver::answerLogin(const QString& input) throw(XmlError) {
	unsigned int status = 0;
	QDomDocument xml;
	xml.setContent(input);
	QDomNodeList domAnswerLogin = xml.elementsByTagName("answerLogin");
	if(domAnswerLogin.size()) {
		QDomNodeList domStatus = domAnswerLogin.item(0).toElement().elementsByTagName("status");
		if(domStatus.size()) {
			status = domStatus.item(0).toElement().text().toInt();
			emit sigLogin(status);
			return;
		}
	}
	throw XmlError("AnswerLogin XML error", input);
}

/**
 * <answerLogoff>
 * 	<status>0</status>
 * </answerLogoff>
 */

void Receiver::answerLogoff(const QString& input) throw(XmlError) {
	unsigned int status = 0;
	QDomDocument xml;
	xml.setContent(input);
	QDomNodeList domAnswerLogoff = xml.elementsByTagName("answerLogoff");
	if(domAnswerLogoff.size()) {
		QDomNodeList domStatus = domAnswerLogoff.item(0).toElement().elementsByTagName("status");
		if(domStatus.size()) {
			status = domStatus.item(0).toElement().text().toInt();
			emit sigLogoff(status);
			return;
		}
	}
	throw XmlError("AnswerLogoff XML error", input);
}

/**
 *  <answerDownload>
 *    <file> 
 *      <name>texto</name>
 *      <path>/home/thiago/</path>
 *      <status>0</status>
 *    </file>
 *  </answerDownload>
 */

void Receiver::answerDownload(const QString& input) throw(XmlError) {
	unsigned int status = 0;
	QString name;
	QString path;
	
	QDomDocument xml;
	xml.setContent(input);
	// <answerDownload>
	QDomNodeList domAnswerDownload = xml.elementsByTagName("answerDownload");
	
	if(domAnswerDownload.size()) {
		getDomNodeValue(AnswerDownload, status, "status", .toInt()); // <status>
		getDomNodeValue(AnswerDownload, name, "name",); // <name>
		getDomNodeValue(AnswerDownload, path, "path",); // <path>
		File file(name, path);
		emit sigDownload(status, file);
		return;
	}
	throw XmlError("AnswerDownload XML error", input);
}

/**
 * <answerUpload>
 * 	 <path>/documentos</path>
 * 	 <name>texto</name>
 * 	 <version>4</version>
 * 	 <status>0</status>
 * </answerUpload>
 */

void Receiver::answerUpload(const QString& input) throw(XmlError) {
	unsigned int status = 0;
	QString name;
	QString path;
	unsigned int version = 0;
	
	QDomDocument xml;
	xml.setContent(input);
	// <AnswerUpload>
	QDomNodeList domAnswerUpload = xml.elementsByTagName("answerUpload");
	
	if(domAnswerUpload.size()) {
		getDomNodeValue(AnswerUpload, status, "status", .toInt()); // <status>
		getDomNodeValue(AnswerUpload, name, "name",); // <name>
		getDomNodeValue(AnswerUpload, path, "path",); // <path>
		getDomNodeValue(AnswerUpload, version, "version", .toInt()); // <version>
		if(name.isEmpty())
			throw XmlError("AnswerUpload XML error. <name> empty.", input);

		File file(name, path, 0, version);
		emit sigUpload(status, file);
		return;
	}
	
	throw XmlError("AnswerUpload XML error", input);
}

/**
 * <answerList>
 * 	<path>/documentos</path>
 * 	<file> 
 * 		<name>texto</name>
 * 		<size>6</size>
 * 		<version>1</version>
 * 	</file>
 * 	<file> 
 * 		<name>texto2</name>
 * 		<size>7</size>
 * 		<version>256</version>
 * 	</file>
 * 	<folder>
 * 		<name>importantes</name>
 * 		<size>100000001</size>
 * 		<itens>45</itens>
 * 	</folder>
 * 	<status>0</status>
 * </answerList>
 */

void Receiver::answerList(const QString& input) throw(XmlError) {
	unsigned int status = 0;
	QString path;
	QList<File> files;
	QList<Folder> folders;
	QString name;
	unsigned int size = 0;
	unsigned int version = 0;
	unsigned int itens = 0;

	QDomDocument xml;
	xml.setContent(input);
	// <answerList>
	QDomNodeList domAnswerList = xml.elementsByTagName("answerList");
	if(domAnswerList.size()) {
		getDomNodeValue(AnswerList, status, "status", .toInt()); // <status>
		getDomNodeValue(AnswerList, path, "path", ); // <path>

		QDomNodeList domFiles = domAnswerList.item(0).toElement().elementsByTagName("file");
		int numberOfFiles = domFiles.size();
		while(numberOfFiles--) {
			name = QString();
			QDomNode domFile = domFiles.item(numberOfFiles);
			getDomNodeValue2(File, name, "name",); // <name>
			getDomNodeValue2(File, size, "size", .toInt()); // <size>
			getDomNodeValue2(File, version, "version", .toInt()); // <version>
			if(!name.isEmpty())
				files.push_back(File(name, path, size, version));
		}
		
		QDomNodeList domFolders = domAnswerList.item(0).toElement().elementsByTagName("folder");
		int numberOfFolders = domFolders.size();
		while(numberOfFolders--) {
			name = QString();
			QDomNode domFolder = domFolders.item(numberOfFolders);
			getDomNodeValue2(Folder, name, "name",.trimmed()); // <name>
			getDomNodeValue2(Folder, size, "size", .toInt()); // <size>
			getDomNodeValue2(Folder, itens, "itens", .toInt()); // <itens>
			if(!name.isEmpty())
				folders.push_back(Folder(name, path, "", itens));
		}
		
		emit sigList(status, path, files, folders);
		return;
	}
	throw XmlError("AnswerList XML error", input);
}

/**
 * <answerMove>
 *   <oldPath>/videos</oldPath>
 *   <oldName>video</oldName>
 *	 <newPath>/videos/tutoriais</newPath>
 *	 <newName>videoSD</newName>
 *	 <status>0</status>
 * </answerMove>
 */

void Receiver::answerMove(const QString& input) throw(XmlError) {
	unsigned int status = 0;
	QString oldPath;
	QString oldName;
	QString newPath;
	QString newName;

	cout << input.toStdString() << endl;
	QDomDocument xml;
	xml.setContent(input);
	// <answerMovef>
	QDomNodeList domAnswerMove = xml.elementsByTagName("answerMove");

	if(domAnswerMove.size()) {
		getDomNodeValue(AnswerMove, status, "status", .toInt()); // <status>
		getDomNodeValue(AnswerMove, oldPath, "oldPath",); // <oldPath>
		getDomNodeValue(AnswerMove, oldName, "oldName",); // <oldName>
		getDomNodeValue(AnswerMove, newPath, "newPath",); // <newPath>
		getDomNodeValue(AnswerMove, newName, "newName",); // <newName>

		emit sigMove(status,oldName + "/" + oldPath, newName + "/" + newPath);
		return;
	}

	throw XmlError("AnswerMove XML error", input);
}

/**
 * <answerDelete>
 * 	<path>/documentos</path>
 * 	<name>texto</name>
 * 	<status>0</status>
 * </answerDelete>
 */

void Receiver::answerDelete(const QString& input) throw(XmlError) {
	unsigned int status = 0;
	QString name;
	QString path;
	
	QDomDocument xml;
	xml.setContent(input);
	// <answerDelete>
	QDomNodeList domAnswerDelete = xml.elementsByTagName("answerDelete");
	
	if(domAnswerDelete.size()) {
		getDomNodeValue(AnswerDelete, status, "status", .toInt()); // <status>
		getDomNodeValue(AnswerDelete, name, "name",); // <name>
		getDomNodeValue(AnswerDelete, path, "path",); // <path>
		QString fullPath = path + "/" + name;
		emit sigDelete(status, fullPath);
		return;
	}
	
	throw XmlError("AnswerDelete XML error", input);
}

/**
 * <answerMkdir>
 * 	<path>/documentos</path>
 * 	<name>legais</name>
 * 	<status>1</status>
 * </answerMkdir>
 */

void Receiver::answerMkdir(const QString& input) throw(XmlError) {
	unsigned int status = 0;
	QString name;
	QString path;

	QDomDocument xml;
	xml.setContent(input);
	// <answerMkdir>
	QDomNodeList domAnswerMkdir = xml.elementsByTagName("answerMkdir");

	if(domAnswerMkdir.size()) {
		getDomNodeValue(AnswerMkdir, status, "status", .toInt()); // <status>
		getDomNodeValue(AnswerMkdir, name, "name",); // <name>
		getDomNodeValue(AnswerMkdir, path, "path",); // <path>

		emit sigMkdir(status, Folder(name, path));
		return;
	}

	throw XmlError("AnswerMkdir XML error", input);
}

/**
 * <answerCopy>
 * 	<path>/documentos</path>
 * 	<name>texto</name>
 * 	<copyPath>/documentos/importantes</copyPath>
 * 	<status>0</status>
 * </answerCopy>
 */

void Receiver::answerCopy(const QString& input) throw(XmlError) {
	unsigned int status = 0;
	QString name;
	QString path;
	QString copyPath;
	
	QDomDocument xml;
	xml.setContent(input);
	// <answerCopy>
	QDomNodeList domAnswerCopy = xml.elementsByTagName("answerCopy");
	
	if(domAnswerCopy.size()) {
		getDomNodeValue(AnswerCopy, status, "status", .toInt()); // <status>
		getDomNodeValue(AnswerCopy, name, "name",); // <name>
		getDomNodeValue(AnswerCopy, path, "path",); // <path>
		getDomNodeValue(AnswerCopy, copyPath, "copyPath",); // <copyPath>
		
		emit sigCopy(status, copyPath, File(name, path, 0, 0/*version*/));
		return;
	}
	
	throw XmlError("AnswerCopy XML error", input);
}

/**
 * <answerVersion>
 * 	<path>/documentos</path>
 * 	<name>texto</name>
 * 	<version>4</version>
 * 	<status>0</status>
 * </answerVersion>
 */

void Receiver::answerVersion(const QString& input) throw(XmlError) {
	unsigned int status = 0;
	QString name;
	QString path;
	unsigned int version = 0;

	QDomDocument xml;
	xml.setContent(input);
	// <answerVersion>
	QDomNodeList domAnswerVersion = xml.elementsByTagName("answerVersion");

	if(domAnswerVersion.size()) {
		getDomNodeValue(AnswerVersion, status, "status", .toInt()); // <status>
		getDomNodeValue(AnswerVersion, name, "name",); // <name>
		getDomNodeValue(AnswerVersion, path, "path",); // <path>
		getDomNodeValue(AnswerVersion, version, "version", .toInt()); // <version>

		emit sigVersion(status, File(name, path, 0, version));
		return;
	}

	throw XmlError("AnswerVersion XML error", input);
}


void Receiver::receive(QNetworkReply *reply) throw(XmlError) {
	QByteArray data = reply->readAll();
	QString input(data);
	QDomDocument xml;
	xml.setContent(input);
	QDomNodeList query;

	try {
		if(isFile(reply,data)) return; //isFile returns true if a file is coming.
	}
	catch(...) {
		throw;
	}
	queryAnswer(Login, "Login");
	queryAnswer(Logoff, "Logoff");
	queryAnswer(Upload, "Upload");
	queryAnswer(List, "List");
	queryAnswer(Delete, "Delete");
	queryAnswer(Version, "Version");
	queryAnswer(Copy, "Copy");
	queryAnswer(Mkdir,"Mkdir");
	queryAnswer(Download,"Download");

	throw	XmlError("Unknown XML error", input); // queryAnswer should return, if not? XmlError!
}

bool Receiver::isFile(QNetworkReply *const reply, const QByteArray& data) throw(XmlError) {
	if(reply->header(QNetworkRequest::ContentTypeHeader).toString() != "text/xml") { //FIXME
		if(reply->hasRawHeader("Content-Disposition")) {
			QString downloadInfo = QString(reply->rawHeader("Content-Disposition"));
//			cout << "downloadInfo :" << downloadInfo.toStdString() << endl;
			downloadInfo = downloadInfo.right(downloadInfo.size() - 22);
			downloadInfo = downloadInfo.left(downloadInfo.size()-1);
			int lastBar = downloadInfo.lastIndexOf('/');
			QString path;
			if(lastBar != -1)
				path = "/" + downloadInfo.left(lastBar);
			else
				path = "/";
			QString nameAndVersion = downloadInfo.right(downloadInfo.size() - lastBar - 1);
			QString name = nameAndVersion.left(nameAndVersion.lastIndexOf('_'));
			unsigned int version = (nameAndVersion.right(nameAndVersion.size() - nameAndVersion.lastIndexOf('_') - 1)).toUInt();
			if(!name.isEmpty()) {
				File downloadedFile(name, path, data.size(),version);
				downloadedFile.setData(data);
				emit sigDownload(0,downloadedFile);
			}
			else {
				throw XmlError(string("Download error. Filename unset: ") + path.toStdString() + string("/") + name.toStdString() );
			}
		}
		return true;
	}
	return false;
}

}
